WWDC Quick Look 💓 By SwiftGGTeam
What’s new in Safari Web Extensions

What’s new in Safari Web Extensions

Watch original video

Highlight

This Session focuses on the improvements to Web Extensions in Safari 16. The biggest topic is Manifest V3 migration support.If you have the Chrome/Firefox extension and are already doing the MV3 migration, Safari will be a lot easier to adapt.


Core Content

The difficulty with Safari Web Extensions is usually not the first version, but continued compatibility.

An extension must serve Mac, iPhone, and iPad at the same time, and it must also keep up with the changes in the extension platforms of Chrome, Firefox, and Edge.Manifest V3 changed the backend model, changed the script injection API, and tightened resource exposure and content security policies.If there are too many differences between browsers, maintenance costs will quickly rise.

The core information of this session is: Safari 15.4 supports Manifest V3 extensions, and Safari 16 has completed a set of Web Extensions APIs and provides a cross-device synchronization experience.Existing Manifest V2 extensions are still supported; extensions ready to be migrated can gradually adopt service workers,scriptingdeclarativeNetRequestexternally_connectableandunlimitedStorage

Detailed Content

Manifest V3: The background page becomes event-driven

(01:02) Manifest V3 is the next version of the Web Extensions platform.Safari 15.4 and later can run the Manifest V3 extension.Apple also stated that Safari will continue to support the Manifest V2 extension.

One of the key changes in Manifest V3 is that extensions can use service workers instead of background pages.service worker is an event-driven page, usingaddEventListenerRegister a listener.Safari also allows the continued use of background pages, but they must be non-persistent.

When migrating, the smallest first step is to change the manifest structure.

{
  "manifest_version": 3,
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_icon": {
      "16": "Images/icon16.png"
    },
    "default_title": "defaultTitle"
  }
}

Key points:

  • manifest_versionChange to3, extended into Manifest V3 rules.
  • background.service_workerPoint to the background script and let the background logic start based on events.
  • actionMerged in Manifest V2browser_actionandpage_action
  • default_iconanddefault_titleContinuing with the description of the default display of toolbar buttons.

scripting: script injection is no longer written in the string

(02:03) Manifest V3 removes the ability to execute JavaScript and insert styles fromtabsAPI moved to newscripting API。tabs.executeScriptNot available in Manifest V3,scriptingAvailable for both Manifest V2 and V3.

The old way of writing can only stuff code into a string.

// Manifest version 2
browser.tabs.executeScript(1, {
  frameId: 1,
  code: "document.body.style.background = 'blue';"
});

Key points:

  • browser.tabs.executeScriptIt’s an old API.
  • The first parameter specifies the tab.
  • frameIdOnly one frame can be specified.
  • codePassing in a string makes complex logic difficult to maintain.

The new writing method can pass functions, parameters and target frames.

// Manifest version 3
function changeBackgroundColor(color) {
  document.body.style.background = color;
};

browser.scripting.executeScript({
  target: { tabId: 1, frameIds: [ 1 ] },
  func: changeBackgroundColor,
  args: [ "blue" ]
});

Key points:

  • changeBackgroundColorIt is an ordinary function and is no longer written as a string.
  • target.tabIdRequired information; no tab ID will return an error.
  • target.frameIdsMultiple frames can be specified.
  • funcPointer to the function to be injected.
  • argsPass parameters to the injection function.

Script files can also be injected with multiple scripts at once.

// Manifest version 3
browser.scripting.executeScript({
  target: { tabId: 1 },
  files: [ "file.js", "file2.js" ]
});

Key points:

  • target.tabIdSpecify the page on which the script will run.
  • filesAccepts arrays, suitable for splitting complex logic into multiple files.
  • This replacestabs.executeScriptThe old limitation of specifying only one file at a time.

Styles also follow the same set of APIs.

// Add styling
browser.scripting.insertCSS({
  target: { tabId: 1, frameIds: [ 1, 2, 3 ] },
  files: [ "file.css", "file2.css" ]
});

// Remove styling
browser.scripting.removeCSS({
  target: { tabId: 1, frameIds: [ 1, 2, 3 ] },
  files: [ "file.css", "file2.css" ]
});

Key points:

  • insertCSSInject style files.
  • removeCSSRemove injected style files.
  • frameIdsAllows processing of multiple frames at once.
  • filesKeep style splits clear.

The Sea Creator example in Session shows the actual migration steps: change the manifest to version 3, andbrowser_actionChange toaction, againbrowser.tabs.executeScriptChange tobrowser.scripting.executeScript.When the first run fails, the console reportsbrowser.tabs.executeScript is undefined, the reason is that the old API is not available in Manifest V3.

web_accessible_resources: Resources are only open to specified sites

(04:43) Manifest V2’s web_accessible_resources only declares a file array. The problem is that web pages may be able to access these resources whenever the extension has access rights.

Manifest V3 changed to resource and matching rule binding.

// Manifest version 3
"web_accessible_resources": [
    {
      "resources": [ "pie.png" ],
      "matches": [ "*://*.apple.com/*" ]
    },
    {
      "resources": [ "cookie.png" ],
      "matches": [ "*://*.webkit.org/*" ]
    }
]

Key points:

  • resourcesSpecify the extended resources to be made available.
  • matchesIndicate which web page URLs can access these resources.
  • pie.pngOnly open toapple.com URL。
  • cookie.pngOnly open towebkit.orgpage.

Content security policies are also changed to object form.

// Manifest version 3

"content_security_policy" : { "extension_pages" : "script-src 'unsafe-eval' 'self'" }

Key points:

  • Manifest V3 declares with objectscontent_security_policy
  • extension_pagesIs the policy key for the extension page.
  • Remote script sources are no longer allowed in Manifest V3.

declarativeNetRequest: Hand over request modifications to Safari

10:03declarativeNetRequestis the content blocking API.The extension only declares a ruleset, and Safari performs the work of intercepting and modifying network requests.

Rule sets can be written in the manifest.

// manifest.json

"permissions": [ "declarativeNetRequest" ],

"declarative_net_request": {
  "rule_resources": [
    {
      "id": "my_ruleset",
      "enabled": true,
      "path": "rules.json"
    }
  ]
}

Key points:

  • permissionsStatement heredeclarativeNetRequestpermissions.
  • declarative_net_request.rule_resourcesList the rule set.
  • idIs the rule set identifier.
  • enabledControls whether the rule set is enabled.
  • pathPoints to the actual rules file.

Safari now allows up to 50 rule sets declared in the manifest; a maximum of 10 can be enabled at the same time.This allows for more flexible rule organization for extensions with multiple filtering modes.

(11:13) Two new dynamic update APIs were also added this year.

// Rules that won't persist

browser.declarativeNetRequest.updateSessionRules({ addRules: [ rule ] });

// Rules that will persist

browser.declarativeNetRequest.updateDynamicRules({ addRules: [ rule ] });

Key points:

  • updateSessionRulesAdd or remove rules for the current session.
  • session rules are not persisted across browser sessions or extension updates.
  • updateDynamicRulesUpdates will preserve the rules.
  • dynamic rules can update interception rules without publishing the entire extension update.

The demonstration scenario is very specific: Sea Creator first uses rules to block images from all URLs, and then usesupdateSessionRulesallowwebkit.org/blog-filesThe page loads images.The result is that WebKit blog image recovery shows that Wikipedia page images are still blocked.

externally_connectable: The web page can communicate with the extension

14:17externally_connectableAllows websites to create custom behaviors when users enable extensions.Before use, you need to declare match patterns in the manifest to determine which pages can communicate with the extension.

Apple specifically stated two conditions: This ability only usesbrowsernamespace; The user must grant the extension permission to access the page before the extension can send and receive messages.

Send messages on the web page.

// In the webpage
let extensionID = "com.apple.Sea-Creator.Extension (GJT7Q2TVD9)";

browser.runtime.sendMessage(extensionID, { greeting: "Hello!" },
 function(response) {
    console.log("Received response from the background page:");
    console.log(response.farewell);
});

Key points:

  • extensionIDUse extended bundle identifier and team identifier combinations.
  • browser.runtime.sendMessageSend a message to the extension.
  • The second parameter is the message body.
  • The callback function handles the response returned by the extension.

Extend the background page to listen to external messages.

// In the background page
browser.runtime.onMessageExternal.addListener(function(message, sender, sendResponse) {
    console.log("Received message from the sender:");
    console.log(message.greeting);
    sendResponse({ farewell: "Goodbye!" });
});

Key points:

  • onMessageExternalReceive external messages from web pages.
  • messageIt is the data passed in from the web page.
  • senderDescribe the source.
  • sendResponseSend the response back to the web page.

When publishing across browsers, the same extension may have multiple store identities.The web page must first confirm that the user has installed the Safari Web Extension.

// Determining the correct identifier

function determineExtensionID(extensionID) {
  return new Promise((resolve) => {
    try {
      browser.runtime.sendMessage(extensionID, { action: 'determineID' }, function(response) {
        if (response)
          resolve({ extensionID: extensionID, isInstalled: true, response: response });
        else 
          resolve({ extensionID: extensionID, isInstalled: false });
      });
    }
  });
};

Key points:

  • determineExtensionIDSend a probe message to a single candidate ID.
  • browser.runtime.sendMessagesenddetermineIDaction.
  • Return when there is a responseisInstalled: true
  • Returned when there is no responseisInstalled: false
  • Session is recommended to cooperate when there are multiple IDsPromise.allBroadcast the probe and find out the installed extensions from the results.

The extension side needs to respond to the probe message.

// background.js

browser.runtime.onMessageExternal.addListener(function(message, sender, sendResponse) {
  if (message.action == "determineID") {
    sendResponse({ "Installed" });
  }
});

Key points:

  • Background script listens for external messages.
  • Process onlyactionfordetermineIDnews.
  • sendResponseTells the web page that the extension is installed.

unlimitedStorage: Storage quota is no longer 10 MB

(17:28) Safari’s unlimitedStorage is now truly unlimited. Extensions are no longer subject to the 10 MB quota.

The enablement method is short.

// manifest.json

"permissions": [ "storage", "unlimitedStorage" ]

Key points:

  • storageEnable extended storage API.
  • unlimitedStorageRemoved 10 MB quota limit.
  • Users can still clear data used by the extension at any time.
  • Apple recommends saving only necessary data to prevent users from actively cleaning it.

Safari 16: Extended cross-device sync

(18:19) Safari 16 improves the extended cross-device experience.After the user opens the extension on one device, the Extension Settings of other devices will prompt for download; it will be automatically enabled after downloading.

For this experience to work, Apple recommends listing iOS, iPadOS, and macOS together when submitting to the App Store.There are two ways to set up synchronization.

The first is universal purchase.Users purchase only once and use the same extension across platforms.When setting up, you need to have the extension use a single bundle identifier so that it is associated with the same app record in App Store Connect.

The second is to manually link the app.In Xcode’s Info.plist, add corresponding bundle identifiers for iOS app, iOS extension, macOS app, and macOS extension.The demonstration of Session is to fill in the app and extension identifiers of the corresponding platform in the settings of each target.

Core Takeaways

1. Make a Manifest V3 migration checker

  • What to do: Scan extension source code and listtabs.executeScriptbrowser_action、Manifest V2 content_security_policyWaiting for migration point.
  • Why it’s worth doing: The failure of Sea Creator in Session comes frombrowser.tabs.executeScript is undefined, such problems can be discovered before building.
  • How ​​to start: Frommanifest.jsonparsemanifest_versionbrowser_actionpage_actioncontent_security_policy, and then scan the scriptbrowser.tabs.executeScript, giving the correspondingscripting.executeScriptModification suggestions.

2. Add site-level switch to content blocking extension

  • What: Let the user temporarily allow a site to load images, scripts or requests in a popup.
  • Why it’s worth doing:updateSessionRulesYou can add non-persistent rules, suitable for “release this website for this session”.
  • How ​​to start: Configure in manifestdeclarativeNetRequest, the default rule is placed inrules.json, called when the user clicks releasebrowser.declarativeNetRequest.updateSessionRules({ addRules: [rule] })

3. Provide an entrance for the website to “check whether the extension is installed”

  • What: The website detects whether the Safari Web Extension exists when the user accesses it, and displays extension-specific features if it exists.
  • Why it’s worth doing:externally_connectableAllows communication between web pages and extensions, and is suitable for websites and extensions to cooperate to complete processes such as login, saving, annotation, and content enhancement.
  • How ​​to start: Use the web pagebrowser.runtime.sendMessage(extensionID, { action: 'determineID' })Detection, used to extend the backgroundbrowser.runtime.onMessageExternal.addListenerreply.

4. Make a pre-release checklist for cross-device extensions

  • What to do: Check whether apps and extensions for iOS, iPadOS, and macOS have the required configurations for cross-device downloading and activation.
  • Why it’s worth doing: Safari 16 will bring user-enabled extensions to other devices, but only if the App Store and bundle identifier are configured correctly.
  • How ​​to start: Universal purchase is preferred; if it cannot be adopted, fill in the bundle identifiers of the corresponding platform for the app and extension in Xcode’s Info.plist.

5. Change the big data extension to a “cleanable” storage model

  • What to do: Design storage occupation panel and clean button for password management, web page clipping, reading annotation and other extensions.
  • Why it’s worth doing:unlimitedStorageCancel the 10 MB quota, but users can still clear extended data.
  • How ​​to start: Manifest statement"permissions": [ "storage", "unlimitedStorage" ], the storage interface retains only necessary data and displays cache, index and user content usage.

Comments

GitHub Issues · utterances