Skip to content

Fix: Scrum report injection on Outlook and Yahoo in Firefox (#135)#279

Closed
saurabh24thakur wants to merge 3 commits intofossasia:mainfrom
saurabh24thakur:fix-issue-135-firefox-injection
Closed

Fix: Scrum report injection on Outlook and Yahoo in Firefox (#135)#279
saurabh24thakur wants to merge 3 commits intofossasia:mainfrom
saurabh24thakur:fix-issue-135-firefox-injection

Conversation

@saurabh24thakur
Copy link
Copy Markdown

@saurabh24thakur saurabh24thakur commented Dec 25, 2025

📌 Fixes

Fixes # (Use "Fixes", "Closes", or "Resolves" for automatic closing)


📝 Summary of Changes

  • Short description of what was changed
  • Include links to related issues/discussions if any

📸 Screenshots / Demo (if UI-related)

Add screenshots, video, or link to deployed preview if applicable


✅ Checklist

  • I’ve tested my changes locally
  • I’ve added tests (if applicable)
  • I’ve updated documentation (if applicable)
  • My code follows the project’s code style guidelines

👀 Reviewer Notes

Add any special notes for the reviewer here

Summary by Sourcery

Improve HTML content injection into email editors for Outlook and Yahoo, with Firefox-specific handling.

Bug Fixes:

  • Ensure Scrum report content is correctly injected into Outlook email editors when using Firefox.
  • Ensure Scrum report content is correctly injected into Yahoo email editors when using Firefox.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Dec 25, 2025

Reviewer's Guide

Adjusts the email content injection logic for Outlook and Yahoo to handle Firefox-specific behavior using execCommand-based insertion and conditional event dispatching so Scrum reports paste correctly across clients.

Sequence diagram for Firefox-aware email content injection

sequenceDiagram
    participant EmailClientAdapter
    participant Browser
    participant OutlookEditor
    participant YahooEditor

    EmailClientAdapter->>Browser: detectClient()
    Browser-->>EmailClientAdapter: clientType
    EmailClientAdapter->>EmailClientAdapter: config = clientConfigs[clientType]
    EmailClientAdapter->>Browser: navigator.userAgent
    Browser-->>EmailClientAdapter: userAgent
    EmailClientAdapter->>EmailClientAdapter: isFirefox = userAgent.includes(firefox)

    alt injectMethod is focusAndPaste (Outlook)
        EmailClientAdapter->>OutlookEditor: focus()
        alt isFirefox
            EmailClientAdapter->>Browser: execCommand(insertHTML, false, content)
            alt execCommand fails
                EmailClientAdapter->>OutlookEditor: innerHTML = content
            end
        else not Firefox
            EmailClientAdapter->>OutlookEditor: innerHTML = content
        end
        EmailClientAdapter->>OutlookEditor: dispatchElementEvents([input, change], true)
    else injectMethod is setContent (Yahoo)
        alt isFirefox
            EmailClientAdapter->>YahooEditor: focus()
            EmailClientAdapter->>Browser: execCommand(insertHTML, false, content)
            alt execCommand fails
                EmailClientAdapter->>YahooEditor: innerHTML = content
            end
            EmailClientAdapter->>YahooEditor: dispatchElementEvents([input, change])
        else not Firefox
            EmailClientAdapter->>YahooEditor: innerHTML = content
            EmailClientAdapter->>YahooEditor: focus()
            EmailClientAdapter->>Browser: getSelection()
            Browser-->>EmailClientAdapter: selection
            EmailClientAdapter->>Browser: createRange()
            Browser-->>EmailClientAdapter: range
            EmailClientAdapter->>Browser: range.selectNodeContents(element)
            EmailClientAdapter->>Browser: selection.removeAllRanges()
            EmailClientAdapter->>Browser: selection.addRange(range)
            EmailClientAdapter->>YahooEditor: dispatchElementEvents([input, change])
        end
    end
Loading

Updated class diagram for EmailClientAdapter Firefox injection logic

classDiagram
    class EmailClientAdapter {
        +clientConfigs
        +detectClient() string
        +injectContent(element, content) void
        +dispatchElementEvents(element, eventTypes, bubbles) void
    }

    class BrowserEnvironment {
        +navigatorUserAgent string
        +execCommand(command, ui, value) bool
        +getSelection() Selection
        +createRange() Range
    }

    class Selection {
        +removeAllRanges() void
        +addRange(range) void
    }

    class Range {
        +selectNodeContents(element) void
    }

    EmailClientAdapter --> BrowserEnvironment : uses
    EmailClientAdapter ..> Selection : uses
    EmailClientAdapter ..> Range : uses
Loading

File-Level Changes

Change Details Files
Add Firefox-specific handling for Outlook's focusAndPaste injection path.
  • Detect Firefox via navigator.userAgent and store in a local isFirefox flag.
  • When using the focusAndPaste injection method, attempt to insert HTML using document.execCommand('insertHTML') in Firefox before falling back to assigning innerHTML.
  • Retain the existing focus and event dispatch behavior after content insertion.
src/scripts/emailClientAdapter.js
Add Firefox-specific handling for Yahoo's setContent injection path.
  • Branch the setContent path on the isFirefox flag to use execCommand-based HTML insertion in Firefox.
  • In Firefox, focus the element, attempt execCommand('insertHTML'), and fall back to innerHTML, then dispatch input/change events.
  • Preserve the existing Yahoo selection manipulation and event dispatch path for non-Firefox browsers to ensure the editor recognizes content changes.
src/scripts/emailClientAdapter.js

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions github-actions Bot added javascript Pull requests that update javascript code core labels Dec 25, 2025
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 security issues, 1 other issue, and left some high level feedback:

Security issues:

  • User controlled data in methods like innerHTML, outerHTML or document.write is an anti-pattern that can lead to XSS vulnerabilities (link)
  • User controlled data in a element.innerHTML is an anti-pattern that can lead to XSS vulnerabilities (link)

General comments:

  • The Firefox-specific insertion logic is duplicated between the focusAndPaste and setContent branches; consider extracting a shared helper for insertHTML/innerHTML fallback to keep behavior consistent and easier to maintain.
  • In the Yahoo setContent path, the non-Firefox branch forces selection updates while the Firefox branch does not; if this difference is intentional, a short comment would help future readers understand why selection handling is skipped on Firefox.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The Firefox-specific insertion logic is duplicated between the `focusAndPaste` and `setContent` branches; consider extracting a shared helper for `insertHTML`/`innerHTML` fallback to keep behavior consistent and easier to maintain.
- In the Yahoo `setContent` path, the non-Firefox branch forces selection updates while the Firefox branch does not; if this difference is intentional, a short comment would help future readers understand why selection handling is skipped on Firefox.

## Individual Comments

### Comment 1
<location> `src/scripts/emailClientAdapter.js:182` </location>
<code_context>
+				if (isFirefox) {
</code_context>

<issue_to_address>
**question (bug_risk):** Firefox `setContent` branch no longer forces selection/range; confirm this won’t break Yahoo-specific behavior.

Previously this path relied on forcing a selection/range update so Yahoo’s editor would register the content change. The new Firefox-specific path only fires `input`/`change` events, which may change behavior for Yahoo in Firefox versus other browsers. Can you confirm this remains correct on Yahoo in Firefox? If so, consider adding a brief note (PR or code comment) to document that this divergence from other browsers is intentional.
</issue_to_address>

### Comment 2
<location> `src/scripts/emailClientAdapter.js:184` </location>
<code_context>
						element.innerHTML = content;
</code_context>

<issue_to_address>
**security (javascript.browser.security.insecure-document-method):** User controlled data in methods like `innerHTML`, `outerHTML` or `document.write` is an anti-pattern that can lead to XSS vulnerabilities

*Source: opengrep*
</issue_to_address>

### Comment 3
<location> `src/scripts/emailClientAdapter.js:184` </location>
<code_context>
						element.innerHTML = content;
</code_context>

<issue_to_address>
**security (javascript.browser.security.insecure-innerhtml):** User controlled data in a `element.innerHTML` is an anti-pattern that can lead to XSS vulnerabilities

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/scripts/emailClientAdapter.js
Comment thread src/scripts/emailClientAdapter.js Outdated
@vedansh-5
Copy link
Copy Markdown
Member

@saurabh24thakur Please add screenshots/screen recordings

@saurabh24thakur
Copy link
Copy Markdown
Author

@saurabh24thakur Please add screenshots/screen recordings

https://drive.google.com/file/d/1feLIyQIth2ehxsk9U_rm4__ieUrv0WBz/view?usp=sharing

@vedansh-5 here is the recording...

@mariobehling
Copy link
Copy Markdown
Member

Uploading the video here for documentation purposes. Please avoid Google drive links here. We prefer to keep documentation on one platform. Thanks

scrum-helper.illustration.mp4

@mariobehling
Copy link
Copy Markdown
Member

Following FOSSASIA best practices, changes should be proposed via an issue before submitting a pull request. The issue should clearly describe the problem and the intended approach. Once the issue exists, the pull request should reference it using the standard closing syntax, for example: “Resolves #123”. This ensures proper tracking, discussion, and review of the change.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes issues with Scrum report content injection into Outlook and Yahoo email editors when using Firefox. The changes refactor HTML content insertion to use browser-specific approaches, particularly leveraging execCommand('insertHTML') for Firefox and maintaining DOM-based insertion for other browsers.

Changes:

  • Introduced helper methods _insertHtmlContent and _safeSetHTML for safer HTML injection
  • Added Firefox-specific handling using execCommand('insertHTML')
  • Updated Outlook and Yahoo email editor injection logic with browser-specific paths

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* Note: Content is assumed to be trusted (generated by extension).
*/
_insertHtmlContent(element, content) {
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Browser detection using user agent strings (navigator.userAgent) is declared twice within the same method - once at line 158 for the _insertHtmlContent function, and then again at line 201 inside the setContent case. Consider extracting this check into a method-level or class-level variable to avoid redundant computation and improve maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +201 to +219
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

if (isFirefox) {
// Firefox: execCommand handles selection updates automatically.
this._insertHtmlContent(element, content);
this.dispatchElementEvents(element, ['input', 'change']);
} else {
// Chrome/Edge: Direct assignment requires manual selection update
// to ensure the editor recognizes the change.
this._safeSetHTML(element, content);

const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);

this.dispatchElementEvents(element, ['input', 'change']);
}
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Browser detection is duplicated in the setContent case at line 201, even though _insertHtmlContent already contains this logic. The browser detection should be removed from line 201 and the method should rely on _insertHtmlContent to handle the Firefox-specific logic consistently.

Suggested change
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (isFirefox) {
// Firefox: execCommand handles selection updates automatically.
this._insertHtmlContent(element, content);
this.dispatchElementEvents(element, ['input', 'change']);
} else {
// Chrome/Edge: Direct assignment requires manual selection update
// to ensure the editor recognizes the change.
this._safeSetHTML(element, content);
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
this.dispatchElementEvents(element, ['input', 'change']);
}
this._insertHtmlContent(element, content);
this.dispatchElementEvents(element, ['input', 'change']);

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +164

if (isFirefox) {
// Firefox often blocks direct innerHTML access in some contexts or doesn't trigger editor updates.
// execCommand('insertHTML') is the standard way to insert content into a contentEditable.
// It also handles cursor placement and selection updates automatically.
if (!document.execCommand('insertHTML', false, content)) {
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The execCommand API at line 164 is deprecated and may not be supported in future browser versions. While it addresses the current Firefox issue, consider adding a fallback mechanism or a comment acknowledging this deprecation for future maintainability.

Suggested change
if (isFirefox) {
// Firefox often blocks direct innerHTML access in some contexts or doesn't trigger editor updates.
// execCommand('insertHTML') is the standard way to insert content into a contentEditable.
// It also handles cursor placement and selection updates automatically.
if (!document.execCommand('insertHTML', false, content)) {
if (isFirefox) {
// Firefox often blocks direct innerHTML access in some contexts or doesn't trigger editor updates.
// Historically, execCommand('insertHTML') has been the standard way to insert content into a
// contentEditable while preserving cursor placement and selection updates.
// NOTE: document.execCommand is deprecated and may be removed in future browsers.
// We therefore feature-detect its availability and fall back to _safeSetHTML if it is unavailable
// or fails for any reason, to keep this code forwards-compatible.
const hasExecCommand = typeof document.execCommand === 'function';
const supportsInsertHtml =
typeof document.queryCommandSupported === 'function'
? document.queryCommandSupported('insertHTML')
: true; // assume true if we cannot detect support explicitly
if (hasExecCommand && supportsInsertHtml) {
try {
if (!document.execCommand('insertHTML', false, content)) {
this._safeSetHTML(element, content);
}
} catch (e) {
this._safeSetHTML(element, content);
}
} else {

Copilot uses AI. Check for mistakes.
Comment on lines +201 to +219
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

if (isFirefox) {
// Firefox: execCommand handles selection updates automatically.
this._insertHtmlContent(element, content);
this.dispatchElementEvents(element, ['input', 'change']);
} else {
// Chrome/Edge: Direct assignment requires manual selection update
// to ensure the editor recognizes the change.
this._safeSetHTML(element, content);

const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);

this.dispatchElementEvents(element, ['input', 'change']);
}
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic in the setContent case (lines 203-219) duplicates the browser-specific behavior already handled by _insertHtmlContent. The Firefox branch calls _insertHtmlContent which will perform another browser check, while the Chrome/Edge branch could simply call _safeSetHTML and handle selection separately. This creates inconsistent logic flow.

Suggested change
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (isFirefox) {
// Firefox: execCommand handles selection updates automatically.
this._insertHtmlContent(element, content);
this.dispatchElementEvents(element, ['input', 'change']);
} else {
// Chrome/Edge: Direct assignment requires manual selection update
// to ensure the editor recognizes the change.
this._safeSetHTML(element, content);
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
this.dispatchElementEvents(element, ['input', 'change']);
}
this._insertHtmlContent(element, content);
this.dispatchElementEvents(element, ['input', 'change']);

Copilot uses AI. Check for mistakes.
@vedansh-5
Copy link
Copy Markdown
Member

@saurabh24thakur Thanks for your contribution, Please address copilots' concerns

@vedansh-5
Copy link
Copy Markdown
Member

Contributor has been inactive for more than 2 weeks. I am closing this PR to let others work on the issue.
@saurabh24thakur Please reopen the PR if you want to continue working on the issue.

@vedansh-5 vedansh-5 closed this Feb 3, 2026
@saurabh24thakur
Copy link
Copy Markdown
Author

Contributor has been inactive for more than 2 weeks. I am closing this PR to let others work on the issue. @saurabh24thakur Please reopen the PR if you want to continue working on the issue.

ok i will

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants